package smallfrench.moteur.lexer;

import java.util.ArrayList;

import smallfrench.moteur.lexer.err.ErreurLexemeInattendu;
import smallfrench.moteur.lexer.err.ErreurLexer;
import smallfrench.moteur.lexer.lexemes.Lexeme;
import smallfrench.moteur.lexer.lexemes.TypeLexeme;

/**
 * Analyseur lexical.
 * @see SuperAutomate
 * @author antoine1023
 *
 */
public class Lexer extends SuperAutomate {

	/**
	 * Crée un nouveau {@link Lexer}.
	 */
	public Lexer() {

		Automate[] autos = {
				new AutomateChaine(),
				new AutomateCommentaire(),
				new AutomateSignes(),
				new AutomateNombre(),
				new AutomateIdentificateur(),
		};

		enfants = autos;
	}

	/**
	 * Passe les espaces blancs.
	 * @param lecteur
	 */
	private void passerBlancs(LecteurChaine lecteur) {
		try {
			while (!lecteur.isEof()) {

				char c = lecteur.suivant();

				switch (c) {
				case ' ':
				case '\t':
				case '\n':
				case '\r':
					continue;
				default:
					lecteur.precedent();
					return;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private Lexeme analyser(LecteurChaine lecteur) throws ErreurLexer {

		if (lecteur.isEof())
			return null;

		passerBlancs(lecteur);

		if (lecteur.isEof())
			return null;

		Lexeme t = super.analyser(lecteur, lecteur.getPosition());

		if (t == null) {
			char c = lecteur.suivant();
			lecteur.precedent();
			throw new ErreurLexemeInattendu(
					String.valueOf(c), lecteur.getPosition());
		}
		return t;
	}

	/**
	 * Analyse une chaîne.
	 * 
	 * @param chaine
	 * @param ignorerErreurs Si <code>true</code>, ne s’arrête pas en cas
	 * d’erreur et essaye de reprendre l’analyse.
	 * @param inclureCommentaires Si <code>false</code>, supprime les
	 * commentaires.
	 * @return La liste des lexèmes.
	 * @throws ErreurLexer Si une erreur s’est produite.
	 */
	public ArrayList<Lexeme> analyser(
			String chaine,
			boolean ignorerErreurs,
			boolean inclureCommentaires) throws ErreurLexer {

		LecteurChaine lecteur = new LecteurChaine(chaine);

		ArrayList<Lexeme> at = new ArrayList<Lexeme>();

		for (;;) {

			Lexeme t = null;

			try {

				t = analyser(lecteur);

			} catch(ErreurLexer e) {
				if (ignorerErreurs) {
					if (! lecteur.isEof()) {
						lecteur.suivant();
						continue;
					} else break;
				} else throw e;
			}

			if (t == null)
				break;


			if (inclureCommentaires) {
				at.add(t);
			} else {
				if (t.type != TypeLexeme.COMMENTAIRE_BLOC
						&& t.type != TypeLexeme.COMMENTAIRE_LIGNE) {
					at.add(t);
				}
			}
		}

		return at;
	}
}
